package modelo.tareas;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import modelo.Usuario;
import modelo.estados.Creada;
import modelo.estados.Estado;
import modelo.exception.NoPuedeIniciarException;
import modelo.exception.SameStateException;
import modelo.exception.StatusChangeException;
import modelo.util.MyDate;

/**
 * @author Cristian
 *
 */

public abstract class Tarea extends Observable {
	
	// Variables  *********************************************************** //
	
	private String nombre;
	private int importancia;
	protected Estado estado;
	private Usuario duenio;
	protected Date fechaCreacion;
	protected Date fechaFinalizacion;
	protected Date realizacion;
	protected Date finalizacionEstimada;
	private Tarea predecesor;
	protected int estimacion;
	protected int duracion = 0;
	protected List<Observer> observers = new ArrayList<Observer>();
	
	
	//Constructor *********************************************************** //
	
	public Tarea(String nom, int impo, String finalizacionEstimada,int estimacion){
		this.nombre = nom;
		this.importancia = impo;
		this.estado=  new Creada();
		this.estimacion = estimacion;
		this.fechaCreacion = new Date();
		this.finalizacionEstimada = new MyDate(finalizacionEstimada).getFechaYHora();
	}
	public Tarea(String nom, int impo, String finalizacionEstimada, int estimacion, Tarea predecesor){
		this.nombre = nom;
		this.importancia = impo;
		this.estado=  new Creada();
		this.predecesor = predecesor;
		this.estimacion = estimacion;
		this.fechaCreacion = new Date();
		this.finalizacionEstimada = new MyDate(finalizacionEstimada).getFechaYHora();
	}
	
	//GET&SET     *********************************************************** //
 
	public String getNombre() {
		return nombre;
	}
	
	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
	
	public int getDuracion(){
		return this.duracion;
	}
	
	public void setDuracion(int duracion) {
		this.duracion = duracion;
	}
	
	public int getImportancia() {
		return importancia;
	}
	
	public void setImportancia(int importancia) {
		this.importancia = importancia;
	}
	
	public Estado getEstado(){
		return this.estado;
	}
	public void setObservers(List<Observer> observers) {
		this.observers = observers;
	}
	public List<Observer> getObservers() {
		return observers;
	}
	
	// Estado UPDATE 
	public void setEstado(Estado estado){
		this.estado = estado;
	}
	
	public Usuario getDuenio(){
		return this.duenio;
	}

	public void setDuenio(Usuario duenio){
		this.duenio = duenio;
	}

	public Tarea getPredecesor() {
		return predecesor;
	}

	public void setPredecesor(Tarea predecesor) {
		this.predecesor = predecesor;
	}
	
	public Date getFechaCreacion() {
		return fechaCreacion;
	}

	public void setFechaCreacion(Date fechaCreacion) {
		this.fechaCreacion = fechaCreacion;
	}

	public Date getFechaFinalizacion() {
		return fechaFinalizacion;
	}

	public void setFechaFinalizacion(Date fechaFinalizacion) {
		this.fechaFinalizacion = fechaFinalizacion;
	}
	
	public int getEstimacion() {
		return estimacion;
	}
	
	public void setEstimacion(int d){
		this.estimacion = d;
	}
	
	public Date getRealizacion() {
		return realizacion;
	}
	
	public void setRealizacion(Date realizacion) {
		this.realizacion = realizacion;
	}
	
	public Date getFinalizacionEstimada() {
		return finalizacionEstimada;
	}
	
	public void setFinalizacionEstimada(Date finalizacionEstimada) {
		this.finalizacionEstimada = finalizacionEstimada;
	}
	

	
	//Metodos     *********************************************************** //
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((duenio == null) ? 0 : duenio.hashCode());
		result = prime * result + duracion;
		result = prime * result + ((estado == null) ? 0 : estado.hashCode());
		result = prime * result + estimacion;
		result = prime * result
				+ ((fechaCreacion == null) ? 0 : fechaCreacion.hashCode());
		result = prime
				* result
				+ ((fechaFinalizacion == null) ? 0 : fechaFinalizacion
						.hashCode());
		result = prime
				* result
				+ ((finalizacionEstimada == null) ? 0 : finalizacionEstimada
						.hashCode());
		result = prime * result + importancia;
		result = prime * result + ((nombre == null) ? 0 : nombre.hashCode());
		result = prime * result
				+ ((predecesor == null) ? 0 : predecesor.hashCode());
		result = prime * result
				+ ((realizacion == null) ? 0 : realizacion.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Tarea other = (Tarea) obj;
		if (duenio == null) {
			if (other.duenio != null)
				return false;
		} else if (!duenio.equals(other.duenio))
			return false;
		if (duracion != other.duracion)
			return false;
		if (estado == null) {
			if (other.estado != null)
				return false;
		} else if (!estado.equals(other.estado))
			return false;
		if (estimacion != other.estimacion)
			return false;
		if (fechaCreacion == null) {
			if (other.fechaCreacion != null)
				return false;
		} else if (!fechaCreacion.equals(other.fechaCreacion))
			return false;
		if (fechaFinalizacion == null) {
			if (other.fechaFinalizacion != null)
				return false;
		} else if (!fechaFinalizacion.equals(other.fechaFinalizacion))
			return false;
		if (finalizacionEstimada == null) {
			if (other.finalizacionEstimada != null)
				return false;
		} else if (!finalizacionEstimada.equals(other.finalizacionEstimada))
			return false;
		if (importancia != other.importancia)
			return false;
		if (nombre == null) {
			if (other.nombre != null)
				return false;
		} else if (!nombre.equals(other.nombre))
			return false;
		if (predecesor == null) {
			if (other.predecesor != null)
				return false;
		} else if (!predecesor.equals(other.predecesor))
			return false;
		if (realizacion == null) {
			if (other.realizacion != null)
				return false;
		} else if (!realizacion.equals(other.realizacion))
			return false;
		return true;
	}
	
	/**
	 * @return boolean	3
	 * retorna true si el estado de la tarea es Creada, de lo contrario retorna false
	 */
	public boolean estaCreado(){
		return this.estado.estaCreado();
	}
	
	/**
	 * @return boolean
	 * retorna true si el estado de la tarea es Iniciada, de lo contrario retorna false	
	 */
	public boolean estaIniciado(){
		return this.estado.estaIniciado();
	}
	
	/**
	 * @return boolean
	 * retorna true si el estado de la tarea es EnTrabajo, de lo contrario retorna false	
	 */
	public boolean estaTrabajando(){
		return this.estado.estaTrabajando();
	}
	
	/**
	 * @return boolean
	 * retorna true si el estado de la tarea es Pausada, de lo contrario retorna false	
	 */
	public boolean estaPausado(){
		return this.estado.estaPausado();
	}
	
	/**
	 * @return boolean	
	 * retorna true si el estado de la tarea es Finalizada, de lo contrario retorna false
	 */
	public boolean estaFinalizado(){
		return this.estado.estaFinalizado();
	}
	
	/**
	 * @return boolean
	 * retorna true si el estado de la tarea es Cerrada, de lo contrario retorna false	
	 */
	public boolean estaCerrado(){
		return this.estado.estaCerrado();
	}
	
	
	/**
	 * De acuerdo al estado de los predecesorer, indica si puede o no iniciarse tarea.
	 * Deben estar las tareas predecesoras finalizadas. 
	 * @return boolean.
	 * @throws NoPuedeIniciarException 
	 */
	public boolean puedeIniciar(Tarea tarea) throws NoPuedeIniciarException{
		
		if(tarea.predecesor == null){
			return true;
		}
		else{ if(! tarea.predecesor.estaFinalizado()){
			throw new NoPuedeIniciarException("No puede Iniciar");
				}
			else{
				return puedeIniciar(tarea.predecesor);
				}
		}
	}	
	
	
	/**
	 * Determina el tiempo de realizacion de una Tarea a partir de dos fechas
	 * pasadas como parametro
	 * PRECONDICION: El Date fechaInicio debe pertenecer a una fecha
	 * anterior o igual a la fecha del Date fechaFinal.
	 * @return int
	 */
	@SuppressWarnings("deprecation")
	public int tiempoDeRealizacion(Date fechaInicio, Date fechaFinal) {
		 {
			long diferenciaEnDias = (fechaFinal.getTime()) - (fechaInicio.getTime());
			long diferenciaHoras = Math.abs((fechaFinal.getHours()) - (fechaInicio.getHours()));
			if (fechaInicio.getHours() > fechaFinal.getHours()){
				diferenciaHoras = 24 - (Math.abs((fechaFinal.getHours()) - (fechaInicio.getHours())));}
					
			double horas = Math.floor((diferenciaEnDias / (1000 * 60 * 60 * 24))*24);
					
			return (int) (horas + diferenciaHoras);
					
			}
		}
	
	/**
	 * Metodo abstracto
	 * Si el metodo puedeIniciar(t) es True se setea el estado como iniciado, caso
	 * contrario como un estado Null.
	 * @throws StatusChangeException 
	 * @throws SameStateException 
	 * @throws NoPuedeIniciarException 
	 */
	public abstract void iniciarTarea() throws SameStateException, StatusChangeException, NoPuedeIniciarException;
	
	/**
	 * Metodo abstracto
	 * Se cambia el estado a estado cerrado.
	 * @throws StatusChangeException 
	 * @throws SameStateException 
	 */
	public abstract void cerrarTarea() throws SameStateException, StatusChangeException;
	
	/**
	 * Metodo abstracto
	 * Se cambia el estado a estado pausado.
	 * @throws StatusChangeException 
	 * @throws SameStateException 
	 */	
	public abstract void pausarTarea() throws SameStateException, StatusChangeException;
	
	/**
	 * Metodo abstracto
	 * Se cambia el estado a estado iniciado
	 * @throws StatusChangeException 
	 * @throws SameStateException 
	 * @throws NoPuedeIniciarException 
	 */
	public abstract String reabrirTarea(String nota) throws SameStateException, StatusChangeException, NoPuedeIniciarException;
	
	/**
	 * Metodo abstracto.
	 * Cambia el estado a estado finalizado
	 * @throws StatusChangeException 
	 * @throws SameStateException 
	 */
	public abstract void finalizarTarea() throws SameStateException, StatusChangeException;
	
	/**
	 * Metodo abstracto.
	 * @throws StatusChangeException 
	 * @throws SameStateException 
	 */
	public abstract void trabajarTarea() throws SameStateException, StatusChangeException;

	/**
	 * Metodo abstracto.
	 * Determina el tiempo que le falta para terminar la tarea.
	 */
	public abstract int porcentajeDeRealizacion();
	
	/**
	 * Metodo abstracto. Retorna las tareas que las componen.
	 * @return List<Tarea>
	 */
	public abstract List<Tarea> getTareas();

}
